using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Threading;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using Microsoft.BizTalk.Message.Interop;
using Microsoft.BizTalk.Component.Interop;
using Microsoft.BizTalk.TransportProxy.Interop;
using Microsoft.XLANGs.BaseTypes;

namespace Microsoft.Samples.BizTalk.Adapter.Tcp.Receive
{
    /// <summary>
    ///     This class represents an individual receive location.
    /// </summary>
    /// <author>
    ///     Paul Brown
    /// </author>
    public class ReceiveLocation
    {
        #region Public Static Members

        // The context property for inbound transport location
        public static readonly PropertyBase InboundTransportLocationProperty = new BTS.InboundTransportLocation();

        // The context property for inbound transport type
        public static readonly PropertyBase InboundTransportTypeProperty = new BTS.InboundTransportType();

        // The context property for ack type
        public static readonly PropertyBase AckType = new BTS.AckType();

        // The BizTalk Party properties
        public static readonly string BizTalkPartyAliasName = TraceHelper.ResourceManager.GetString(ResourceConstants.BizTalkPartyAliasName);
        public static readonly string BizTalkPartyAliasQualifierDnsName = TraceHelper.ResourceManager.GetString(ResourceConstants.BizTalkPartyAliasQualifierDnsName);
        public static readonly string BizTalkPartyAliasQualifierIpAddress = TraceHelper.ResourceManager.GetString(ResourceConstants.BizTalkPartyAliasQualifierIpAddress);
        public static readonly string BizTalkPartyAliasQualifierDuplexAddress = TraceHelper.ResourceManager.GetString(ResourceConstants.BizTalkPartyAliasQualifierDuplexAddress);

        #endregion

        #region Private Instance Members

        private ReceiveAdapter      _adapter;               // The receive adapter
        private string              _uri;                   // The address that the receive location will listen on
        private int                 _listenPort;            // The port that this receive location will listen on for incoming connections
        private int                 _maxPendingConnections; // The maximum number of pending connections allowed on the socket
        private bool                _isDuplex;              // Is this receive location set up to support duplex connections
        private bool                _isTwoWay;              // Is the receive location set up as a request-response message exchange pattern
        private int                 _listenerTerminateTimeout; // The number of milliseconds to wait before aborting non-responsive threads when the adapter is terminating
        private int                 _pollTimeout;           // The poll timeout for the duplex listener in microseconds
        private int                 _mainCloseTimeout;      // The close timeout for the close socket operation in seconds
        private int                 _duplexCloseTimeout;    // The close timeout for the close duplex socket operation in seconds
        private int                 _clientCloseTimeout;    // The close timeout for the close client socket operation in seconds
        private Listener            _listener;              // The main listener for this receive location
        private int                 _socketReceiveBufferSize; // The socket receive buffer size in bytes
        private int                 _socketReceiveTimeout;  // The socket receive timeout in milliseconds
        private int                 _duplexReceiveTimeout;  // The socket receive timeout in milliseconds when duplex receiving
        private int                 _socketSendBufferSize;  // The socket send buffer size in bytes
        private int                 _socketSendTimeout;     // The socket send timeout in milliseconds
        private int                 _receiveDataBufferSize; // The size of the data buffer used to pass to the socket for receiving
        private int                 _sendDataBufferSize;    // The size of the data buffer used to pass to the socket for sending
        private int                 _responseMessageTimeout; // The timeout in minutes of a response message passed from BizTalk
        private Encoding            _dataEncoding;          // The encoding of the data being received
        private Encoding            _frameEncoding;         // The encoding of the frames
        private byte[]              _frameStart;            // The message start frame (encoded)
        private byte[]              _frameEnd;              // The message end frame (encoded)
        private bool                _isOrdered;             // Will the receive location support ordered delivery?
        private bool                _suspendSubsequentMessages; // Will the receive location suspend all subsequent messages?
        private bool                _acceptMultipleMessages;// Will the receive location support accepting multiple messages down the same socket if not duplex?

        // Delegate used to call Stop() method asynchronously on listener
        private delegate void StopMethod();

        #endregion

        #region Constructor

        /// <summary>
        ///     This constructor initializes the endpoint with the basic configuration.
        /// </summary>
        public ReceiveLocation(ReceiveAdapter adapter, string uri, XmlDocument locationConfiguration, IPropertyBag bizTalkConfiguration)
        {
            // Validate arguments
            if (adapter == null) throw new ArgumentNullException("adapter");
            if (locationConfiguration == null) throw new ArgumentNullException("locationConfiguration");
            if (bizTalkConfiguration == null) throw new ArgumentNullException("bizTalkConfiguration");

            // Initialize members
            this._adapter = adapter;
            this._uri = uri;
            this._listenerTerminateTimeout = 2000;
            this._listenPort = 1969;
            this._maxPendingConnections = 5;
            this._pollTimeout = 1000;
            this._mainCloseTimeout = 0;
            this._duplexCloseTimeout = 0;
            this._clientCloseTimeout = 0;
            this._socketReceiveBufferSize = 8192;
            this._socketReceiveTimeout = 500;
            this._duplexReceiveTimeout = 250;
            this._socketSendBufferSize = 8192;
            this._socketSendTimeout = 500;
            this._receiveDataBufferSize = 2048;
            this._sendDataBufferSize = 2048;
            this._responseMessageTimeout = 20;
            this._isDuplex = false;
            this._isTwoWay = false;
            this._listener = null;
            this._dataEncoding = null;
            this._frameEncoding = null;
            this._frameStart = null;
            this._frameEnd = null;
            this._isOrdered = false;
            this._suspendSubsequentMessages = true;
            this._acceptMultipleMessages = false;

            // Load configuration
            this.LoadReceiveLocationConfiguration(locationConfiguration, bizTalkConfiguration);
        }

        #endregion
        
        #region Public Instance Properties

        /// <summary>
        ///     This property returns the address that the endpoint will listen on for incoming connections.
        /// </summary>
        public string Uri
        {
            get { return (this._uri); }
        }

        /// <summary>
        ///     This property returns the number of milliseconds that is used to wait for the listeners to finish
        ///     while the adapter is terminating.  They are aborted if the timeout period expires.
        /// </summary>
        public int ListenerTerminateTimeout
        {
            get { return (this._listenerTerminateTimeout); }
        }

        /// <summary>
        ///     This property returns the well-known port that this receive location will listen for incoming connections.
        /// </summary>
        public int ListenPort
        {
            get { return (this._listenPort); }
        }

        /// <summary>
        ///     This property returns the maximum number of pending connections allowed on the listener socket.
        /// </summary>
        public int MaxPendingConnections
        {
            get { return (this._maxPendingConnections); }
        }

        /// <summary>
        ///     This property returns the poll timeout in microseconds when checking for new connections on the duplex listener thread.
        /// </summary>
        public int PollTimeout
        {
            get { return (this._pollTimeout); }
        }

        /// <summary>
        ///     This property returns the close timeout in seconds when closing the main listener socket.
        /// </summary>
        public int MainCloseTimeout
        {
            get { return (this._mainCloseTimeout); }
        }

        /// <summary>
        ///     This property returns the close timeout in seconds when closing the duplex listener socket.
        /// </summary>
        public int DuplexCloseTimeout
        {
            get { return (this._duplexCloseTimeout); }
        }

        /// <summary>
        ///     This property returns the close timeout in seconds when closing a client socket that has connected
        ///     to the duplex listener.
        /// </summary>
        public int ClientCloseTimeout
        {
            get { return (this._clientCloseTimeout); }
        }

        /// <summary>
        ///     This property returns the receive buffer size in bytes for the underlying socket.
        /// </summary>
        public int SocketReceiveBufferSize
        {
            get { return (this._socketReceiveBufferSize); }
        }

        /// <summary>
        ///     This property returns the receive timeout in milliseconds for receive operations on the socket.
        /// </summary>
        public int SocketReceiveTimeout
        {
            get { return (this._socketReceiveTimeout); }
        }

        /// <summary>
        ///     This property returns the receive timeout in milliseconds for duplex receive operations on the socket.
        /// </summary>
        public int DuplexReceiveTimeout
        {
            get { return (this._duplexReceiveTimeout); }
        }

        /// <summary>
        ///     This property returns the send buffer size in bytes for the underlying socket.
        /// </summary>
        public int SocketSendBufferSize
        {
            get { return (this._socketSendBufferSize); }
        }

        /// <summary>
        ///     This property returns the send timeout in milliseconds for send operations on the socket.
        /// </summary>
        public int SocketSendTimeout
        {
            get { return (this._socketSendTimeout); }
        }

        /// <summary>
        ///     This property returns the receive data buffer in bytes when receiving data from the socket.
        /// </summary>
        public int ReceiveDataBufferSize
        {
            get { return (this._receiveDataBufferSize); }
        }

        /// <summary>
        ///     This property returns the send data buffer in bytes when sending data to the socket.
        /// </summary>
        public int SendDataBufferSize
        {
            get { return (this._sendDataBufferSize); }
        }

        /// <summary>
        ///     This property returns the timeout in minutes that BizTalk will wait up to for the response message
        ///     before sending a nack to the adapter.
        /// </summary>
        public int ResponseMessageTimeout
        {
            get { return (this._responseMessageTimeout); }
        }

        /// <summary>
        ///     This property returns whether this receive location supports duplex connections.
        /// </summary>
        public bool IsDuplex
        {
            get { return (this._isDuplex); }
        }

        /// <summary>
        ///     This property returns whether this receive location should operate in a request-response pattern.
        /// </summary>
        public bool IsTwoWay
        {
            get { return (this._isTwoWay); }
        }

        /// <summary>
        ///     This property returns a reference to the receive adapter.
        /// </summary>
        public ReceiveAdapter Adapter
        {
            get { return (this._adapter); }
        }

        /// <summary>
        ///     This property returns the encoding used for the messages.
        /// </summary>
        public Encoding DataEncoding
        {
            get { return (this._dataEncoding); }
        }

        /// <summary>
        ///     This property returns the encoding used for the frames.
        /// </summary>
        public Encoding FrameEncoding
        {
            get { return (this._frameEncoding); }
        }

        /// <summary>
        ///     This property returns the delimiter byte sequence used as the start frame that has been encoded using the
        ///     frame encoding.
        /// </summary>
        public byte[] FrameStart
        {
            get { return (this._frameStart); }
        }

        /// <summary>
        ///     This property returns the delimiter byte sequence used as the end frame that has been encoded using the
        ///     frame encoding.
        /// </summary>
        public byte[] FrameEnd
        {
            get { return (this._frameEnd); }
        }

        /// <summary>
        ///     This property determines whether the messages should be ordered when submitting to BizTalk.
        /// </summary>
        public bool IsOrdered
        {
            get { return (this._isOrdered); }
        }

        /// <summary>
        ///     This property determines whether any subsequent messages should be suspended if a message fails
        ///     to be submitted to preserve order.
        /// </summary>
        public bool SuspendSubsequentMessages
        {
            get { return (this._suspendSubsequentMessages); }
        }

        /// <summary>
        ///     This property determines whether the adapter will accept multiple messages down a non-duplex socket.
        /// </summary>
        public bool AcceptMultipleMessages
        {
            get { return (this._acceptMultipleMessages); }
        }

        #endregion

        #region Public Static Methods

        /// <summary>
        ///     This method creates a BizTalk message and applies context properties specific to the receive adapter.
        /// </summary>
        public static IBaseMessage CreateMessage(Stream stream, IBaseMessageFactory messageFactory, ReceiveLocation receiveLocation)
        {
            // Create message part to contain the body of the message
            IBaseMessagePart part = messageFactory.CreateMessagePart();
            part.Data = stream;

            // Create message
            IBaseMessage message = messageFactory.CreateMessage();
            message.AddPart("body", part, true);

            // Create message context
            message.Context.Write(ReceiveLocation.InboundTransportLocationProperty.Name.Name,
                                  ReceiveLocation.InboundTransportLocationProperty.Name.Namespace,
                                  receiveLocation.Uri);
            message.Context.Write(ReceiveLocation.InboundTransportTypeProperty.Name.Name,
                                  ReceiveLocation.InboundTransportTypeProperty.Name.Namespace,
                                  receiveLocation.Adapter.TransportType);

            //
            // Add other context specific properties to the adapter for the request message
            //

            // Promoted
            message.Context.Promote(ConfigurationHelper.ListenPort, ConfigurationHelper.AdapterPropertyNamespace, receiveLocation.ListenPort);
            message.Context.Promote(ConfigurationHelper.IsDuplex, ConfigurationHelper.AdapterPropertyNamespace, receiveLocation.IsDuplex);
            message.Context.Promote(ConfigurationHelper.IsOrdered, ConfigurationHelper.AdapterPropertyNamespace, receiveLocation.IsOrdered);

            // Written (not promoted)
            message.Context.Write(ConfigurationHelper.ClientCloseTimeout, ConfigurationHelper.AdapterPropertyNamespace, receiveLocation.ClientCloseTimeout);
            message.Context.Write(ConfigurationHelper.CloseTimeout, ConfigurationHelper.AdapterPropertyNamespace, receiveLocation.MainCloseTimeout);
            message.Context.Write(ConfigurationHelper.DataEncoding, ConfigurationHelper.AdapterPropertyNamespace, receiveLocation.DataEncoding.EncodingName);
            message.Context.Write(ConfigurationHelper.DataEncodingPreamble, ConfigurationHelper.AdapterPropertyNamespace, ConfigurationHelper.GetHexString(receiveLocation.DataEncoding.GetPreamble()));
            message.Context.Write(ConfigurationHelper.DuplexCloseTimeout, ConfigurationHelper.AdapterPropertyNamespace, receiveLocation.DuplexCloseTimeout);
            message.Context.Write(ConfigurationHelper.DuplexPollTimeout, ConfigurationHelper.AdapterPropertyNamespace, receiveLocation.PollTimeout);
            message.Context.Write(ConfigurationHelper.FrameEncoding, ConfigurationHelper.AdapterPropertyNamespace, (receiveLocation.FrameEncoding == null ? TraceHelper.ResourceManager.GetString(ResourceConstants.ReceiveBinaryEncoding) : receiveLocation.FrameEncoding.EncodingName));
            message.Context.Write(ConfigurationHelper.FrameEncodingPreamble, ConfigurationHelper.AdapterPropertyNamespace, (receiveLocation.FrameEncoding == null ? string.Empty : ConfigurationHelper.GetHexString(receiveLocation.FrameEncoding.GetPreamble())));
            message.Context.Write(ConfigurationHelper.FrameEndDelimiter, ConfigurationHelper.AdapterPropertyNamespace, ConfigurationHelper.GetHexString(receiveLocation.FrameEnd));
            message.Context.Write(ConfigurationHelper.FrameStartDelimiter, ConfigurationHelper.AdapterPropertyNamespace, ConfigurationHelper.GetHexString(receiveLocation.FrameStart));
            message.Context.Write(ConfigurationHelper.ListenerTerminateTimeout, ConfigurationHelper.AdapterPropertyNamespace, receiveLocation.ListenerTerminateTimeout);
            message.Context.Write(ConfigurationHelper.MaxPendingConnections, ConfigurationHelper.AdapterPropertyNamespace, receiveLocation.MaxPendingConnections);
            message.Context.Write(ConfigurationHelper.ReceiveDataBufferSize, ConfigurationHelper.AdapterPropertyNamespace, receiveLocation.ReceiveDataBufferSize);
            message.Context.Write(ConfigurationHelper.ResponseMessageTimeout, ConfigurationHelper.AdapterPropertyNamespace, receiveLocation.ResponseMessageTimeout);
            message.Context.Write(ConfigurationHelper.SendDataBufferSize, ConfigurationHelper.AdapterPropertyNamespace, receiveLocation.SendDataBufferSize);
            message.Context.Write(ConfigurationHelper.SocketReceiveBufferSize, ConfigurationHelper.AdapterPropertyNamespace, receiveLocation.SocketReceiveBufferSize);
            message.Context.Write(ConfigurationHelper.SocketReceiveTimeout, ConfigurationHelper.AdapterPropertyNamespace, receiveLocation.SocketReceiveTimeout);
            message.Context.Write(ConfigurationHelper.SocketSendBufferSize, ConfigurationHelper.AdapterPropertyNamespace, receiveLocation.SocketSendBufferSize);
            message.Context.Write(ConfigurationHelper.SocketSendTimeout, ConfigurationHelper.AdapterPropertyNamespace, receiveLocation.SocketSendTimeout);
            message.Context.Write(ConfigurationHelper.SuspendSubsequentMessages, ConfigurationHelper.AdapterPropertyNamespace, receiveLocation.SuspendSubsequentMessages);

            // Return message
            return (message);
        }

        #endregion

        #region Public Instance Methods

        /// <summary>
        ///     This method starts the main listener.
        /// </summary>
        public void Start()
        {
            // Perform some basic configuration checking
            if (this.IsTwoWay && this.IsDuplex)
            {
                // Request-Response receive location and duplex support at the same time is not supported!
                string message = TraceHelper.FormatEntry(ResourceConstants.ReceiveTwoWayPortAndDuplexNotSupported, this.Uri);
                TraceHelper.WriteRawEntry(TraceLevel.Error, message);
                throw new ArgumentException(message);
            }

            // Create and start listener
            this._listener = new Listener(this.Adapter, this);
            this._listener.ThreadTerminated += new EventHandler(this.Listener_ThreadTerminated);
            this._listener.Start();
        }

        /// <summary>
        ///     This method stops the main listener and a duplex listener if active.  It will block until it has stopped.
        /// </summary>
        public void Stop()
        {
            // Unsubscribe from event
            this._listener.ThreadTerminated -= new EventHandler(this.Listener_ThreadTerminated);

            // Stop listener
            this._listener.Stop();
        }

        #endregion

        #region Private Instance Event Handlers

        /// <summary>
        ///     This event handler is called whenever the listener thread has terminated unexpectedly without
        ///     first calling Stop() in this class.
        /// </summary>
        private void Listener_ThreadTerminated(object sender, EventArgs e)
        {
            // Invoke Stop() on listener object asynchronously to avoid running on same thread as the event invocation
            Listener listener = (Listener)sender;
            StopMethod stopMethod = new StopMethod(listener.Stop);

            // Invoke method
            IAsyncResult result = stopMethod.BeginInvoke(null, null);

            // Wait for it to finish
            result.AsyncWaitHandle.WaitOne();

            // Clear listener
            this._listener = null;
        }

        #endregion

        #region Private Instance Methods

        /// <summary>
        ///     This method initializes this instance with the receive location configuration.
        /// </summary>
        private void LoadReceiveLocationConfiguration(XmlDocument locationConfiguration, IPropertyBag bizTalkConfiguration)
        {
            // Listener Terminate Timeout
            XmlNode node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/receiveListenerTerminateTimeout");
            this._listenerTerminateTimeout = int.Parse(node.InnerText);
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveListenerTerminateTimeout, this.Uri, this.ListenerTerminateTimeout);

            // TCP Listen Port
            node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/tcpListenPort");
            this._listenPort = int.Parse(node.InnerText);
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveTcpListenPort, this.Uri, this.ListenPort);

            // TCP Max Pending Connections
            node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/tcpMaxPendingConnections");
            this._maxPendingConnections = int.Parse(node.InnerText);
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveTcpMaxPendingConnections, this.Uri, this.MaxPendingConnections);

            // TCP Is Duplex
            node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/tcpIsDuplex");
            this._isDuplex = bool.Parse(node.InnerText);
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveTcpIsDuplex, this.Uri, this.IsDuplex);

            // TCP Poll Timeout
            node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/tcpDuplexPollTimeout");
            this._pollTimeout = int.Parse(node.InnerText);
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveDuplexPollTimeout, this.Uri, this.PollTimeout);

            // TCP Main Socket Close Timeout
            node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/tcpCloseTimeout");
            this._mainCloseTimeout = int.Parse(node.InnerText);
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveMainCloseTimeout, this.Uri, this.MainCloseTimeout);

            // TCP Duplex Socket Close Timeout
            node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/tcpDuplexCloseTimeout");
            this._duplexCloseTimeout = int.Parse(node.InnerText);
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveDuplexCloseTimeout, this.Uri, this.DuplexCloseTimeout);

            // TCP Client Socket Close Timeout
            node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/tcpClientCloseTimeout");
            this._clientCloseTimeout = int.Parse(node.InnerText);
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveClientCloseTimeout, this.Uri, this.ClientCloseTimeout);

            // TCP Socket Receive Buffer Size
            node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/tcpSocketReceiveBufferSize");
            this._socketReceiveBufferSize = int.Parse(node.InnerText);
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveSocketReceiveBufferSize, this.Uri, this.SocketReceiveBufferSize);

            // TCP Socket Receive Timeout
            node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/tcpSocketReceiveTimeout");
            this._socketReceiveTimeout = int.Parse(node.InnerText);
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveSocketReceiveTimeout, this.Uri, this.SocketReceiveTimeout);

            // Duplex Socket Receive Timeout
            node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/tcpDuplexReceiveTimeout");
            this._duplexReceiveTimeout = int.Parse(node.InnerText);
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveDuplexReceiveTimeout, this.Uri, this.DuplexReceiveTimeout);

            // TCP Socket Send Buffer Size
            node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/tcpSocketSendBufferSize");
            this._socketSendBufferSize = int.Parse(node.InnerText);
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveSocketSendBufferSize, this.Uri, this.SocketSendBufferSize);

            // TCP Socket Send Timeout
            node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/tcpSocketSendTimeout");
            this._socketSendTimeout = int.Parse(node.InnerText);
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveSocketSendTimeout, this.Uri, this.SocketSendTimeout);

            // Receive Data Buffer Size
            node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/receiveBufferSize");
            this._receiveDataBufferSize = int.Parse(node.InnerText);
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveReceiveDataBufferSize, this.Uri, this.ReceiveDataBufferSize);

            // Send Data Buffer Size
            node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/sendBufferSize");
            this._sendDataBufferSize = int.Parse(node.InnerText);
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveSendDataBufferSize, this.Uri, this.SendDataBufferSize);

            // Response Message Timeout
            node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/sendResponseMessageTimeout");
            this._responseMessageTimeout = int.Parse(node.InnerText);
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveResponseMessageTimeout, this.Uri, this.ResponseMessageTimeout);

            // Data Output Byte Order Mark
            node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/dataOutputBom");
            bool outputBom = bool.Parse(node.InnerText);
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveDataOutputByteOrderMark, this.Uri, outputBom);

            // Data Byte Order
            node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/dataByteOrder");
            EndianTypes byteOrder = (EndianTypes)Enum.Parse(typeof(EndianTypes), node.InnerText);
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveDataByteOrder, this.Uri, byteOrder);

            // Data Custom Encoding
            node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/dataEncodingCustomValue", false);
            string customEncodingValue = (node == null ? string.Empty : node.InnerText);
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveDataCustomEncoding, this.Uri, customEncodingValue);

            // Data Encoding
            node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/dataEncoding");
            this._dataEncoding = ConfigurationHelper.GetEncoding(node, customEncodingValue, byteOrder, outputBom);
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveDataEncoding, this.Uri, this.DataEncoding.ToString());

            // Frame Output Byte Order Mark
            node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/frameOutputBom");
            outputBom = bool.Parse(node.InnerText);
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveFrameOutputByteOrderMark, this.Uri, outputBom);

            // Frame Byte Order
            node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/frameByteOrder");
            byteOrder = (EndianTypes)Enum.Parse(typeof(EndianTypes), node.InnerText);
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveFrameByteOrder, this.Uri, byteOrder);

            // Frame Custom Encoding
            node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/frameEncodingCustomValue", false);
            customEncodingValue = (node == null ? string.Empty : node.InnerText);
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveFrameCustomEncoding, this.Uri, customEncodingValue);

            // Frame Encoding
            node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/frameEncoding");
            this._frameEncoding = ConfigurationHelper.GetEncoding(node, customEncodingValue, byteOrder, outputBom);
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveFrameEncoding, this.Uri, (this.FrameEncoding == null ? TraceHelper.ResourceManager.GetString(ResourceConstants.ReceiveBinaryEncoding) : this.FrameEncoding.ToString()));

            // Frame Start
            node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/frameStartDelimiter");
            if (this.FrameEncoding == null)
            {
                // Binary
                string[] hexValues = node.InnerText.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                this._frameStart = new byte[hexValues.Length];
                for (int i = 0; i < hexValues.Length; i++)
                {
                    this._frameStart[i] = Convert.ToByte(hexValues[i], 16);
                }
            }
            else
            {
                // Text
                this._frameStart = this.FrameEncoding.GetBytes(node.InnerText);
            }
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveFrameStart, this.Uri, node.InnerText);

            // Frame End
            node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/frameEndDelimiter");
            if (this.FrameEncoding == null)
            {
                // Binary
                string[] hexValues = node.InnerText.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                this._frameEnd = new byte[hexValues.Length];
                for (int i = 0; i < hexValues.Length; i++)
                {
                    this._frameEnd[i] = Convert.ToByte(hexValues[i], 16);
                }
            }
            else
            {
                // Text
                this._frameEnd = this.FrameEncoding.GetBytes(node.InnerText);
            }
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveFrameEnd, this.Uri, node.InnerText);

            // Is Ordered
            node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/orderIsOrdered");
            this._isOrdered = bool.Parse(node.InnerText);
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveIsOrdered, this.Uri, this.IsOrdered);

            // Suspend Subsequent Messages
            node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/orderOnFailureStop");
            this._suspendSubsequentMessages = bool.Parse(node.InnerText);
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveSuspendSubsequentMessages, this.Uri, this.SuspendSubsequentMessages);

            // Accept Multiple Messages
            node = ConfigurationHelper.GetEntry(locationConfiguration, "/Config/receiveMultipleMessages");
            this._acceptMultipleMessages = bool.Parse(node.InnerText);
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveAcceptMultipleMessages, this.Uri, this.AcceptMultipleMessages);

            // Two Way Receive Port
            PropertyBase twoWayProperty = new global::BTS.TwoWayReceivePort();
            object twoWay = null;
            bizTalkConfiguration.Read(twoWayProperty.Name.Name, out twoWay, 0);

            if (twoWay == null)
            {
                string message = TraceHelper.FormatEntry(ResourceConstants.ConfigurationPropertyMissing, twoWayProperty.Name.Name);
                TraceHelper.WriteRawEntry(TraceLevel.Error, message);
                throw new AdapterConfigurationException(message);
            }
            else
            {
                this._isTwoWay = (bool)twoWay;
            }
            if (this.Adapter.TraceSwitch.TraceVerbose) TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.ReceiveTwoWayReceivePort, this.Uri, this.IsTwoWay);
        }

        #endregion
    }
}
